using BepuPhysics;
using BepuPhysics.Collidables;
using BepuUtilities;
using DemoContentLoader;
using DemoRenderer;
using DemoRenderer.UI;
using DemoUtilities;
using System;
#if MYCODE
using BepuUtilities.Vectors;
#else
using System.Numerics;
#endif
namespace Demos.Demos
{
    /// <summary>
    /// 一个金字塔的盒子,因为没有金字塔的盒子,你就不可能有一个物理引擎。
    /// </summary>
    public class PyramidDemo : Demo
    {
        public unsafe override void Initialize(ContentArchive content, Camera camera)
        {
            camera.Position = new Vector3(-30, 8, -110);
            camera.Yaw = MathHelper.Pi * 3f / 4;
            camera.Pitch = 0;
            // PositionFirstTimestpper是最简单的时步模式,但由于它在帧开始时将速度积分到位置中,因此直接修改了时间步长之外的速度
            // 将在碰撞检测或解算器有机会介入之前集成。这在这个演示中很好。其他内置选项包括PositionLastTimestpper和SubsteppingTimestpper。
            // 请注意,timeSteper还具有回调,您可以使用这些回调在处理阶段之间执行逻辑,如BeforeCollisionDetection。
            Simulation = Simulation.Create(BufferPool, new DemoNarrowPhaseCallbacks(), new DemoPoseIntegratorCallbacks(new Vector3(0, -10, 0)), new PositionFirstTimestepper());

            var boxShape = new Box(1, 1, 1);
            boxShape.ComputeInertia(1, out var boxInertia);
            var boxIndex = Simulation.Shapes.Add(boxShape);
            const int pyramidCount = 40;// 40;
            for (int pyramidIndex = 0; pyramidIndex < pyramidCount; ++pyramidIndex)
            {
                const int rowCount = 20;
                for (int rowIndex = 0; rowIndex < rowCount; ++rowIndex)
                {
                    int columnCount = rowCount - rowIndex;
                    for (int columnIndex = 0; columnIndex < columnCount; ++columnIndex)
                    {
                        Simulation.Bodies.Add(BodyDescription.CreateDynamic(new Vector3(
                            (-columnCount * 0.5f + columnIndex) * boxShape.Width,
                            (rowIndex + 0.5f) * boxShape.Height,
                            (pyramidIndex - pyramidCount * 0.5f) * (boxShape.Length + 4)),
                            boxInertia,
                            new CollidableDescription(boxIndex, 0.1f),
                            new BodyActivityDescription(0.01f)));
                    }
                }
            }

            //创建一个2500x2500的地面
            Simulation.Statics.Add(new StaticDescription(new Vector3(0, -0.5f, 0), new CollidableDescription(Simulation.Shapes.Add(new Box(2500, 1, 2500)), 0.1f)));
        }

        // 我们会随机化子弹的大小。
        Random random = new Random(5);
        public override void Update(Window window, Camera camera, Input input, float dt)
        {
            if (input != null && input.WasPushed(OpenTK.Input.Key.Z))
            {
                // 创建当用户按下按钮时我们将在金字塔处启动的形状。
                var bulletShape = new Sphere(0.5f + 5 * (float)random.NextDouble());
                // 请注意,使用半径^3表示质量可能会产生一些相当严重的质量比。
                // 观察当一个大球坐在几个质量只有几分之一的盒子上时会发生什么-
                // 碰撞看起来柔滑得多,也不太稳定。对于大多数游戏,如果您想要保持僵化,您需要使用以下一些组合：
                // 1)当重物依赖于轻物时,限制重物质量与轻物质量的比率。
                // 2)使用更短的时间步长,更频繁地更新。
                // 3)使用更多的解算器迭代次数。
                // #2和#3可能会变得非常昂贵。在病理情况下,对于相同的模拟,它最终可能比使用专注于质量的解算器慢。
                // 不幸的是,目前,bepuPhysical v2不包含任何替代解算器,所以如果您不能强行解决问题,
                // 最好的解决办法是尽可能多地作弊,以避免出现转折点。

                var bodyDescription = BodyDescription.CreateConvexDynamic(
                    new Vector3(0, 8, -130), new BodyVelocity(new Vector3(0, 0, 150)), bulletShape.Radius * bulletShape.Radius * bulletShape.Radius, Simulation.Shapes, bulletShape);
                Simulation.Bodies.Add(bodyDescription);
            }
            base.Update(window, camera, input, dt);
        }

        public override void Render(Renderer renderer, Camera camera, Input input, TextBuilder text, Font font)
        {
            text.Clear().Append("Press Z to launch a ball!");
            renderer.TextBatcher.Write(text, new Vector2(20, renderer.Surface.Resolution.Y - 20), 16, new Vector3(1, 1, 1), font);
            base.Render(renderer, camera, input, text, font);
        }

    }
}
